package ServidorDistribuido;

import cliente.EjecutorClass;
import cliente.InterfazRemota;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Hashtable;

public class Servidor {

    static boolean servidorCreado = false;// Si ya entre una vez en el ciclo como no coordinador y cree mis hilos.
    static boolean esCoordinador = true; // Dice si es coordinador. Tambien es la condicion de parada de HiloServidor* y HiloPorServidor*
    static boolean matarHilos = true;// Condicion de parada de HiloServidor*
//    static boolean coordinadorEstaVivo;// Condicion de parada de HiloPorServidor*
//    static boolean condicionParada = true;
//    static boolean multicastVive = true;

    static InetAddress miIp;
    static InetAddress ipServidorCoord;
    static Socket socketCliente = null;
    static ServerSocket socketServidor = null;
    
    public static int relojLogico = 0;// Contador de los eventos que han llegado al coordinador
    public static final int PORT = 3332;
    public static final int SERVERPORT = 4444; //Puerto para conexiones por socket
    public static final int MULTICASTSERVERPORT = 4445; //Puerto de escucha multicast

    public static InetAddress group = null;
    public static MulticastSocket msock = null;

    static Hashtable <InetAddress, Socket> ipSocket =
            new Hashtable <InetAddress, Socket>();
    static Hashtable <InetAddress, Integer> cargaDeTrabajo =
            new Hashtable <InetAddress, Integer>();
    static Hashtable <InetAddress, Boolean> estadoEjecucion =
            new Hashtable <InetAddress, Boolean>();
    //archivosPorServidor.put("file.txt|1", arrayList);
    static Hashtable <String, ArrayList <InetAddress>> archivosPorServidor =
            new Hashtable <String, ArrayList <InetAddress>>();
    static Hashtable <String, Integer> versionArchivo =
            new Hashtable <String, Integer>();

    public Servidor() {
    }

    public static void main (String args[]) throws IOException {
        try{
            try{
                ipServidorCoord = InetAddress.getByName(args[0]);
                esCoordinador = Boolean.parseBoolean(args[1]);
            }catch(Exception exx){
                System.out.println("Error en el pasaje de parámetros. Ejecutar de nuevo así: "
                        + "\n$> java -Djava.rmi.server.hostname=192.168.1.183 "
                        + "ServidorDistribuido.Servidor ipCoordinador esCoordinador(true/false)");
//                exx.printStackTrace();
                System.exit(1);
            }

            System.out.println("S: Comenzo el servidor.");
            
//            SOLO PARA PRUEBAS. DESCOMENTAR LA DE ABAJO DEL IF PARA LA VERSION ORIGINAL
//            ServerSocket socketServidor;
//            if(esCoordinador){
//                socketServidor = new ServerSocket(SERVERPORT);
//            }else{
//                socketServidor = new ServerSocket(7777);
//            }
            
//            ServerSocket socketServidor = new ServerSocket(SERVERPORT);
            

//###############################PRUEBA###################################
//Servidor.estadoEjecucion.put(InetAddress.getByName("192.168.1.101"), false);
//Servidor.estadoEjecucion.put(InetAddress.getByName("localhost"), false);
//Servidor.estadoEjecucion.put(InetAddress.getByName("localhost"), false);
//
//Set servidoresConArchivo = new HashSet();
//servidoresConArchivo.add("192.168.2.3");
//servidoresConArchivo.add("192.168.2.4");
//servidoresConArchivo.add("192.168.2.5");
//versionArchivo.put("file.txt", 1);
//archivosPorServidor.put("file.txt|1", servidoresConArchivo);

//########################################################################

            while(true){
//                Thread.sleep(200);
//                System.out.print("-");
                if (esCoordinador){
                    System.out.println("S: El servidor es coordinador.");
//                    if(socketServidor.isBound()){
//                        System.out.println("socketServidor is bound");
//                        socketServidor.close();
//                    }
                    socketServidor = new ServerSocket(SERVERPORT);
                    servidorCreado = false;
                    while(true){
                        socketCliente = socketServidor.accept();
                        InetAddress ipCliente = socketCliente.getInetAddress();
                        miIp = socketCliente.getLocalAddress();
                        BufferedReader in = new BufferedReader(new InputStreamReader(
                                socketCliente.getInputStream()));
                        String clienteOServidor = in.readLine();
                        if(clienteOServidor.equals("Servidor")){
                            System.out.println("S: Conexion con el Servidor establecida. "
                                + "Dir ip del Servidor: " + ipCliente.toString());
                            Runnable hiloPorServRun = null;
                            Thread hiloPorServ = null;
                            hiloPorServRun = new HiloPorServidorLectura(ipCliente, socketCliente);
                            hiloPorServ = new Thread(hiloPorServRun);
                            hiloPorServ.start();

                            Runnable hiloPorServRun1 = null;
                            Thread hiloPorServ1 = null;
                            hiloPorServRun1 = new HiloPorServidorEscritura(ipCliente, socketCliente);
                            hiloPorServ1 = new Thread(hiloPorServRun1);
                            hiloPorServ1.start();
                            
                            Runnable hiloPorServRun2 = null;
                            Thread hiloPorServ2 = null;
                            hiloPorServRun2 = new HiloMulticastEscritura();
                            hiloPorServ2 = new Thread(hiloPorServRun2);
                            hiloPorServ2.start();

                            ipSocket.put(ipCliente, socketCliente);
//                            HiloPorServidorEscritura.multicastInformacion("ipSocket|"
//                                    + ipCliente.toString().split("[/]")[1] + "|" + socketCliente.toString());
                            estadoEjecucion.put(ipCliente, false);
                            HiloPorServidorEscritura.multicastInformacion("estadoEjecucion|"
                                    + ipCliente.toString().split("[/]")[1] + "|" + false);
                            int carga = (int)(Math.random() * (101));
                            cargaDeTrabajo.put(ipCliente, carga);
                            HiloPorServidorEscritura.multicastInformacion("cargaDeTrabajo|"
                                    + ipCliente.toString().split("[/]")[1] + "|" + carga);

                            HiloPorServidorEscritura.multicastInformacion("relojLogico|" + 1);


                        }else if(clienteOServidor.equals("Cliente")){
                            System.out.println("S: Conexion con el cliente establecida. "
                                + "Dir ip del cliente: " + ipCliente.toString());
                            Runnable nuevoCliente = new HiloEscuchaCliente(ipCliente, socketCliente);
                            Thread hiloCliente = new Thread(nuevoCliente);
                            hiloCliente.start();
                        }
                    }
                }else if (!servidorCreado){//El servidor no es coordinador
                    servidorCreado = true;
                    /* no es verdaderamente socketCliente porque es del servidor, 
                    pero hace de cliente en este caso */
                    Thread.sleep(1000);
                    //PRINT A VER QUE EXPLOTA
                    socketCliente = new Socket(ipServidorCoord, SERVERPORT);
                    ipServidorCoord = socketCliente.getInetAddress();
                    PrintWriter out = new PrintWriter(socketCliente.getOutputStream(), true);
                    out.println("Servidor");
                    out.flush();
                    miIp = socketCliente.getLocalAddress();
                    estadoEjecucion.put(miIp, false);
                    System.out.println("S: Conexion con el coordinador establecida. "
                            + "Dir ip del servidor: " + ipServidorCoord.toString());
                    Runnable hiloServRun = null;
                    Thread hiloServ = null;
                    hiloServRun = new HiloServidorLectura(ipServidorCoord, socketCliente);
                    hiloServ = new Thread(hiloServRun);
                    hiloServ.start();

                    Runnable hiloServRun1 = null;
                    Thread hiloServ1 = null;
                    hiloServRun1 = new HiloServidorEscritura(ipServidorCoord, socketCliente);
                    hiloServ1 = new Thread(hiloServRun1);
                    hiloServ1.start();

                    //RMI
//                    EjecutorClass ejecutor = new EjecutorClass(ipServidorCoord, socketCliente);
//                    InterfazRemota stub = (InterfazRemota) UnicastRemoteObject.exportObject(ejecutor, 0);
//                    Registry registry = LocateRegistry.getRegistry();
//                    registry.rebind("rmiServer", stub);

                    Runnable hiloPorServRun2 = null;
                    Thread hiloPorServ2 = null;
                    hiloPorServRun2 = new HiloMulticastEscritura();
                    hiloPorServ2 = new Thread(hiloPorServRun2);
                    hiloPorServ2.start();
                }
            }
        }catch (Exception e){
            System.out.println("Error en Servidor.java. " + e.toString());
            e.printStackTrace();
        }finally{
            esCoordinador = false;
            System.out.println("Servidor: Termino el Servidor.");
        }
    }
}

//        EJEMPLO BUSQUEDA
//ArrayList servidoresConArchivo = new ArrayList();
//servidoresConArchivo.add("192.168.2.3");
//servidoresConArchivo.add("192.168.2.4");
//servidoresConArchivo.add("192.168.2.5");
//versionArchivo.put("file.txt", 1);
//archivosPorServidor.put("file.txt|1", servidoresConArchivo);

//        EJEMPLO EJECUCION
//        InetAddress prim = InetAddress.getByName("www.google.com");
//        InetAddress prim2 = InetAddress.getByName("www.yahoo.com");
//        InetAddress prim3 = InetAddress.getByName("www.as.com");
//        listaServidores.put(prim, false);
//        listaServidores.put(prim2, false);
//        listaServidores.put(prim3, false);
